package com.zhongli.devplatform.service.impl.user;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.api.R;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sun.org.apache.bcel.internal.generic.IMUL;
import com.zhongli.devplatform.bean.SecurityHelper;
import com.zhongli.devplatform.constant.Consts;
import com.zhongli.devplatform.dto.im.IMUserInitDTO;
import com.zhongli.devplatform.dto.student.BindParentDTO;
import com.zhongli.devplatform.dto.student.StudyReportDetailsDTO;
import com.zhongli.devplatform.dto.student.UpdateBindParentDTO;
import com.zhongli.devplatform.dto.user.UserRegisterDTO;
import com.zhongli.devplatform.enums.SexType;
import com.zhongli.devplatform.enums.UserType;
import com.zhongli.devplatform.mapper.clazz.ReadClazzMapper;
import com.zhongli.devplatform.mapper.im.ImUserMapper;
import com.zhongli.devplatform.mapper.sys.ReadChapterExercisesMapper;
import com.zhongli.devplatform.mapper.sys.ReadRoleMapper;
import com.zhongli.devplatform.mapper.user.*;
import com.zhongli.devplatform.po.common.ReadClazzPO;
import com.zhongli.devplatform.po.im.ImUserPO;
import com.zhongli.devplatform.po.sys.ReadChapterExercisesPO;
import com.zhongli.devplatform.po.view.ViewUserPO;
import com.zhongli.devplatform.po.user.*;
import com.zhongli.devplatform.query.student.StudentQuery;
import com.zhongli.devplatform.service.imp.im.IMRestAPIService;
import com.zhongli.devplatform.service.imp.user.ReadUserService;
import com.zhongli.devplatform.utils.BeanCopierUtil;
import com.zhongli.devplatform.utils.user.GenerateID;
import com.zhongli.devplatform.utils.StringUtils;
import com.zhongli.devplatform.bean.Res;
import com.zhongli.devplatform.vo.parent.ParentVO;
import com.zhongli.devplatform.vo.student.OperationStudentVO;
import com.zhongli.devplatform.vo.student.StudentVO;
import com.zhongli.devplatform.vo.student.UserAndClassInfoVO;
import com.zhongli.devplatform.vo.sys.SubjectVO;
import jodd.util.StringUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import static com.zhongli.devplatform.enums.UserType.Parent;
import static com.zhongli.devplatform.enums.UserType.Student;

/**
 * @author WuXiCheng
 * @version 1.0
 * @date 2020/7/30 12:02
 */
@Service
public class ReadUserServiceImpl extends ServiceImpl<ReadUserMapper, ReadUserPO> implements ReadUserService {

    @Resource
    private ReadUserMapper userMapper;

    @Resource
    private PasswordEncoder passwordEncoder;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private ReadClazzMapper clazzMapper;

    @Resource
    private ReadTeachClazzMapper teachClazzMapper;

    @Resource
    private ReadUserRoleMapper userRoleMapper;

    @Resource
    private ReadRoleMapper roleMapper;

    @Resource
    private ReadUserAttFansMapper userAttFansMapper;

    @Resource
    private ReadUserParentMapper userParentMapper;

    @Resource
    private ReadUserInfoMapper userInfoMapper;

    @Resource
    private ReadChapterExercisesMapper chapterExercisesMapper;

    @Autowired
    private IMRestAPIService imRestAPIService;

    @Autowired
    private ImUserMapper imUserMapper;

    @Transactional
    @Override
    public Res register(UserRegisterDTO userRegisterDTO) {
        ReadRolePO rolePO = roleMapper.selectOne(new QueryWrapper<ReadRolePO>().eq("code", userRegisterDTO.getRole()));
        if (Objects.isNull(rolePO)){
            return Res.not_ok("角色不存在");
        }
        // 验证手机号与验证码
        if (verifyPhoneCode(userRegisterDTO.getPhone(), userRegisterDTO.getCode())) {
            return Res.not_ok("验证码不正确，或已过期");
        }else {
            // 删除该验证码
            stringRedisTemplate.delete(Consts.PHONE_CODE + userRegisterDTO.getPhone());
        }
        // 检查手机号是否已被使用
        if (userMapper.selectOne(new QueryWrapper<ReadUserPO>().eq("account_num",userRegisterDTO.getPhone())) != null) {
            return Res.not_ok("该手机号已被注册");
        }
        int code = 1;
        String message = "注册失败";
        switch (rolePO.getCode()){
            case "student" :
                if (!StringUtils.isEmpty(userRegisterDTO.getInvitationCode())){
                    ReadClazzPO clazzPO = clazzMapper.selectOne(new QueryWrapper<ReadClazzPO>().eq("invitation_code", userRegisterDTO.getInvitationCode()));
                    if (Objects.isNull(clazzPO)){
                        message = "邀请码无效";
                    }else {
                        //添加用户
                        ReadUserPO student = createUser(userRegisterDTO.getPhone(), userRegisterDTO.getPassword(), Student.getCode());
                        student.setClazzId(clazzPO.getId());
                        student.setSchoolId(clazzPO.getSchoolId());
                        student.setSkinType(1);
                        userMapper.insert(student);
                        //添加用户默认信息
                        ReadUserInfoPO userInfo = createUserInfo(student);
                        userInfo.setPhone(userRegisterDTO.getPhone());
                        userInfoMapper.insert(userInfo);
                        //添加角色
                        ReadUserRolePO userRolePO = getReadUserRolePO(student,userRegisterDTO.getRole());
                        userRoleMapper.insert(userRolePO);
                        //初始im用户
                        initImUser(student.getId());
                        code = 0;
                        message = "注册成功";
                    }
                }
                break;
            case "parent" :
                ReadUserPO parent = createUser(userRegisterDTO.getPhone(), userRegisterDTO.getPassword(), UserType.Parent.getCode());
                parent.setSkinType(1);
                userMapper.insert(parent);
                //添加用户默认信息
                ReadUserInfoPO userInfo = createUserInfo(parent);
                userInfo.setPhone(userRegisterDTO.getPhone());
                userInfoMapper.insert(userInfo);
                //添加角色
                ReadUserRolePO userRolePO = getReadUserRolePO(parent,userRegisterDTO.getRole());
                userRoleMapper.insert(userRolePO);
                //初始im用户
                initImUser(parent.getId());
                code = 0;
                message = "注册成功";
        }
        return new Res<>(code,message);
    }

    private void initImUser(Integer userId) {
        ReadUserInfoPO userInfoPO = userInfoMapper.selectOne(new QueryWrapper<ReadUserInfoPO>().eq("user_id", userId));
        //导入账号
        IMUserInitDTO dto = IMUserInitDTO.builder()
                .imId(userId.toString())
                .nickName(userInfoPO.getNickName())
                .headImgUrl(StringUtil.isNotBlank(userInfoPO.getHeadImgUrl()) ? userInfoPO.getHeadImgUrl() : "")
                .build();
        imRestAPIService.initIMUser(dto);
    }

    //添加用户默认信息
    private ReadUserInfoPO createUserInfo(ReadUserPO user) {
        ReadUserInfoPO userInfoPO = new ReadUserInfoPO();
        userInfoPO.setUserId(user.getId());
        userInfoPO.setNickName(user.getAccountNum());
        userInfoPO.setSex(SexType.Male);
        return userInfoPO;
    }

    @Override
    public Res forgetPwd(String phone, String verifyCode, String password) {
        // 验证手机号与验证码
        if (verifyPhoneCode(phone, verifyCode)) {
            return Res.not_ok("验证码不正确，或已过期");
        }else {
            // 删除该验证码
            stringRedisTemplate.delete(Consts.PHONE_CODE + phone);
        }
        String newPwd = passwordEncoder.encode(password);
        ReadUserPO userPO = userMapper.selectOne(new QueryWrapper<ReadUserPO>().eq("account_num", phone));
        userPO.setPassword(newPwd);
        userMapper.updateById(userPO);
        return Res.ok("更改密码成功");
    }

    /**
     * 获取当前用户的粉丝列表
     * @param currentUserId 当前用户id
     * @return 结果
     */
    @Override
    public List<ReadUserPO> getCurrentTeacherFans(Integer currentUserId) {
        List<ReadUserAttFansPO> fansPOS = userAttFansMapper
                .selectList(new QueryWrapper<ReadUserAttFansPO>().eq("to_user_id", currentUserId));
        List<Integer> fromIds = fansPOS
                                .stream()
                                .map(ReadUserAttFansPO::getFromUserId)
                                .collect(Collectors.toList());
        return userMapper.selectBatchIds(fromIds);
    }

    @Override
    @Transactional
    public void updateWeixinId(Integer id, String weixinId) {

        update(new UpdateWrapper<ReadUserPO>()
                .set("weixin_id", null)
                .eq("weixin_id", weixinId)
        );

        update(new UpdateWrapper<ReadUserPO>()
                .set("weixin_id", weixinId)
                .eq("id", id)
        );
    }

    @Override
    public List<ViewUserPO> getAllTeacherByCurrentStuGrade(Integer currentUserId, String nickName) {
        return null;
    }


    @Override
    public List<StudentVO> learnCondition(StudentQuery query) {
        List<StudentVO> learnList = userMapper.findLearnList(query);
        return learnList;
    }

    @Override
    public Res resetPwd(String pwd, String newPwd) {
        Integer userId = SecurityHelper.getCurrentUserId();
        ReadUserPO userPO = userMapper.selectById(userId);
        // 校验之前的密码是否正确
        if (!new BCryptPasswordEncoder().matches(pwd, userPO.getPassword())) {
            return Res.not_ok("原密码错误");
        }
        userPO.setPassword(passwordEncoder.encode(newPwd));
        userMapper.updateById(userPO);
        return Res.ok("修改成功");
    }

    /**
     *
     * 学生绑定家长
     * @param bindParentDTO
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Res bindParent(BindParentDTO bindParentDTO) {
        if (verifyPhoneCode(bindParentDTO.getPhone(), bindParentDTO.getCode())){
            return Res.not_ok("验证码不正确，或已过期");
        }else {
            // 删除该验证码
            stringRedisTemplate.delete(Consts.PHONE_CODE + bindParentDTO.getPhone());
        }
        //查询需要绑定的用户
        ReadUserPO userPO = userMapper.selectOne(new QueryWrapper<ReadUserPO>().eq("account_num",bindParentDTO.getPhone()));
        Integer studentId = SecurityHelper.getCurrentUserId();
        Integer count = userParentMapper.selectCount(new QueryWrapper<ReadUserParentPO>().eq("user_id", studentId));
        if (count>2){
            return Res.not_ok("最多只能绑定两个家长");
        }
        //账号未注册
        if (Objects.isNull(userPO)){
            //创建家长用户并初始化密码
            ReadUserPO parent = createUser(bindParentDTO.getPhone(), "123456", UserType.Parent.getCode());
            userMapper.insert(parent);
            //建立学生家长关系
            ParentToStudent(studentId, parent);
            //添加用户信息
            ReadUserInfoPO userInfo = createUserInfo(parent);
            userInfo.setNickName(bindParentDTO.getNickName());
            userInfo.setPhone(bindParentDTO.getPhone());
            userInfoMapper.insert(userInfo);
            //添加角色
            ReadUserRolePO userRolePO = getReadUserRolePO(parent, "parent");
            userRoleMapper.insert(userRolePO);
            return Res.ok("绑定成功");
        } else if (userPO.getCategory() == Parent.getCode()){
            List<ReadUserParentPO> parentPOS = userParentMapper.selectList(new QueryWrapper<ReadUserParentPO>().eq("parent_id", userPO.getId()));
            if (parentPOS.size()>0){
                return Res.not_ok("该手机号已被绑定过了");
            }
            //如果已经注册是家长，直接绑定
            ParentToStudent(studentId, userPO);
            //更新用户名
            ReadUserInfoPO userInfoPO = userInfoMapper.selectOne(new QueryWrapper<ReadUserInfoPO>().eq("user_id", userPO.getId()));
            userInfoPO.setNickName(bindParentDTO.getNickName());
            userInfoMapper.updateById(userInfoPO);
            return Res.ok("绑定成功");
        }
        return Res.not_ok("该手机号已关联其他角色");
    }

    @Override
    public Res studentListByClazzId(StudentQuery studentQuery) {
        List<OperationStudentVO> operationStudentVOS = userMapper.studentListByClazzId(studentQuery);
        return Res.ok(operationStudentVOS);
    }

    @Override
    public Res getBindParentInfo() {
        Integer userId = SecurityHelper.getCurrentUserId();
        List<ReadUserParentPO> parentPOS = userParentMapper.selectList(new QueryWrapper<ReadUserParentPO>().eq("user_id", userId));
        if (parentPOS.isEmpty()){
            return Res.not_ok("没有绑定家长");
        }
        List<Integer> parentIds = parentPOS.stream().map(ReadUserParentPO::getParentId).collect(Collectors.toList());
        List<ReadUserInfoPO> userInfoPOS = userInfoMapper.selectList(new QueryWrapper<ReadUserInfoPO>().in("user_id", parentIds));
        List<ParentVO> parentVOS = BeanCopierUtil.copy(userInfoPOS, ParentVO.class);

        return Res.ok(parentVOS);
    }

//    @Override
//    public Res studentListByOpeId(StudentQuery studentQuery) {
//        List<StudentOperationVO> studentOperationVOS = userMapper.studentListByOpeId(studentQuery);
//        return Res.ok(studentOperationVOS);
//    }

    //建立家长学生关系
    private void ParentToStudent(Integer studentId, ReadUserPO parent) {
        //添加用户角色关系
        ReadUserRolePO userRolePO = getReadUserRolePO(parent,"parent");
        userRoleMapper.insert(userRolePO);
        //维护学生家长关系
        ReadUserParentPO userParentPO = new ReadUserParentPO();
        userParentPO.setUserId(studentId);
        userParentPO.setParentId(parent.getId());
        userParentMapper.insert(userParentPO);
    }


    /**
     * 老师报表 查询所有信息
     * @param studyReportDetailsDTO {@link StudyReportDetailsDTO}
     * @return 学生列表
     */
    @Override
    public List<UserAndClassInfoVO> getAllStudentByCurrentTeach(StudyReportDetailsDTO studyReportDetailsDTO) {
        studyReportDetailsDTO.setCurrentThreadId(SecurityHelper.getCurrentUserId());
        return userMapper.getAllStudentByCurrentTeach(studyReportDetailsDTO);
    }

    private ReadUserRolePO getReadUserRolePO(ReadUserPO userPO,String userType) {
        //查询用户角色
        ReadRolePO rolePO = roleMapper.selectOne(new QueryWrapper<ReadRolePO>().eq("code", userType));
        //添加角色用户关系
        ReadUserRolePO userRolePO = new ReadUserRolePO();
        userRolePO.setUserId(userPO.getId());
        userRolePO.setRoleId(rolePO.getId());
        return userRolePO;
    }

    /**
     * 检查验证码是否有效
     *
     * @param phone
     * @param code
     * @return
     */
    private boolean verifyPhoneCode(String phone, String code) {
        String cacheCode = stringRedisTemplate.opsForValue().get(Consts.PHONE_CODE + phone);
        return StrUtil.isBlank(cacheCode) || !StrUtil.equals(cacheCode, code);
    }

    /**
     * 新建用户
     * @param phone
     * @param password
     * @param userType
     * @return
     */
    private ReadUserPO createUser(String phone, String password,Integer userType) {
        ReadUserPO userPO = new ReadUserPO();
        userPO.setPassword(passwordEncoder.encode(password));
        userPO.setAccountNum(phone);
        userPO.setIsEnable(true);
        userPO.setCreateTime(new Date());
        userPO.setCategory(userType);
        // 设置用户唯一ID
        String soleId = GenerateID.getGenerateID();
        userPO.setSoleId(soleId);
        return userPO;
    }

    /**
     * 学生---学习进度雷达图
     * @param studentId  学生id
     * @return  返回values
     */
    @Override
    public List<Integer> radarChart(Integer studentId,List<SubjectVO> subjectVOs) {
        List<Integer> values = new ArrayList<>();
        subjectVOs.forEach(item->{
            List<Integer> allKnowledgeIds = userMapper.getAlllearningProgressBySubjectId(studentId,item.getId());
            int allCount = allKnowledgeIds.size();
            if (allCount == 0) return;
            int fished = chapterExercisesMapper.selectCount(new QueryWrapper<ReadChapterExercisesPO>()
                    .eq("student_id", studentId)
                    .in("knowledge_id", allKnowledgeIds));
            if (fished == 0){
                values.add(0);
            }else {
                NumberFormat numberFormat = NumberFormat.getInstance();
                numberFormat.setMaximumFractionDigits(3);
                String format = numberFormat.format((float) fished / (float) allCount);
                values.add((int) (Float.parseFloat(format)*100));
            }
        });
        return values;
    }

    @Override
    public Res updateBindParent(UpdateBindParentDTO updateBindParentDTO) {
        if (verifyPhoneCode(updateBindParentDTO.getNewPhone(),updateBindParentDTO.getCode())){
            return Res.not_ok("验证码不正确，或已过期");
        }else {
            // 删除该验证码
            stringRedisTemplate.delete(Consts.PHONE_CODE + updateBindParentDTO.getNewPhone());
        }
        Integer userId = SecurityHelper.getCurrentUserId();
        ReadUserPO userPO = userMapper.selectOne(new QueryWrapper<ReadUserPO>().eq("account_num", updateBindParentDTO.getOldPhone()));
        if (Objects.isNull(userPO)){
            return Res.not_ok("原家长手机有误");
        }
        ReadUserParentPO userParentPO = userParentMapper.selectOne(new QueryWrapper<ReadUserParentPO>().eq("user_id", userId).eq("parent_id", userPO.getId()));
        if (Objects.isNull(userParentPO)){
            return Res.not_ok("原家长手机没有绑定该学生");
        }else {
            ReadUserPO readUserPO = userMapper.selectOne(new QueryWrapper<ReadUserPO>().eq("account_num", updateBindParentDTO.getNewPhone()));
            if (Objects.isNull(readUserPO)){
                //删除原家长信息
                userParentMapper.delete(new QueryWrapper<ReadUserParentPO>().eq("user_id",userId).eq("parent_id",userPO.getId()));
                //创建家长用户并初始化密码
                ReadUserPO parent = createUser(updateBindParentDTO.getNewPhone(), "123456", UserType.Parent.getCode());
                userMapper.insert(parent);
                //建立学生家长关系
                ParentToStudent(userId, parent);
                //添加用户信息
                ReadUserInfoPO userInfo = createUserInfo(parent);
                userInfo.setNickName(updateBindParentDTO.getNickName());
                userInfo.setPhone(updateBindParentDTO.getNewPhone());
                userInfoMapper.insert(userInfo);
                //添加角色
                ReadUserRolePO userRolePO = getReadUserRolePO(parent, "parent");
                userRoleMapper.insert(userRolePO);
                return new Res<>(0,"绑定成功");
            }
            userParentPO.setParentId(readUserPO.getId());
            //绑定新家长信息
            userParentMapper.update(userParentPO,new UpdateWrapper<ReadUserParentPO>()
                    .eq("user_id",userId)
                    .eq("parent_id",userPO.getId()));
            ReadUserInfoPO userInfoPO = userInfoMapper.selectOne(new QueryWrapper<ReadUserInfoPO>().eq("user_id", readUserPO.getId()));
            userInfoPO.setNickName(updateBindParentDTO.getNickName());
            userInfoMapper.updateById(userInfoPO);
            return new Res<>(0,"绑定成功");
        }
    }

    @Override
    public Res importTeacherImUserId() {
        Integer userId = SecurityHelper.getCurrentUserId();
        ImUserPO imUserPO = imUserMapper.selectOne(new QueryWrapper<ImUserPO>().eq("im_id", userId));
        if (Objects.isNull(imUserPO)){
            this.initImUser(userId);
            return new Res<>(0,"已导入");
        }else {
            return new Res<>(0,"已存在");
        }
    }
}
